package com.cmcc.sdtp.server;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.security.AccessControlException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.jdbc.core.JdbcTemplate;

import com.cmcc.sdtp.sdtp.message.MessageType;
import com.cmcc.sdtp.sdtp.message.SDTPMessage;
import com.cmcc.sdtp.sdtp.message.XDRDataNotifyReqMsg;
import com.cmcc.sdtp.sdtp.util.MessageUtil;
import com.cmcc.xdr.util.ProtocolSpringContextUtils;

public class SDTPRequestThread implements Runnable {

	private SocketChannel channel;

	private boolean flag = false;

	private static SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");

	private static Logger logger = LogManager.getLogger(SDTPRequestThread.class);

	public static Map<String, String> dpiMap = new HashMap<String, String>();
	/**
	 * 距离上次发送间接收的数据包数量。
	 */
	private int recDataInfo = 0;

	/**
	 * 鉴权,版本验证标记
	 */
	private int authFlag = 0;

	public SDTPRequestThread() {

	}

	public SDTPRequestThread(SocketChannel socketChannel) {
		this.channel = socketChannel;
		flag = true;

	}

	public void run() {
		SDTPServer server = SDTPServer.getInstall();
		if (!server.sdtpFlag && channel != null) {
			try {
				// 按文档的意思好像不管出啥问题，只要链接没关都要尝试往客户端发个链接释放的响应来着？
				if (channel.isConnected()) {
					SDTPMessage respMsg = MessageUtil.dealLinkRel(null);
					channel.write(respMsg.getBuffer());
				}
			} catch (IOException e1) {
				logger.error("发送释放连接响应失败", e1);
			} finally {
				release();
			}
			logger.debug(channel.isOpen() + "服务器连接关闭***********************************");
			return;
		}
		String mess = channel.toString();
		String newMess = mess.substring(mess.indexOf("remote=/") + 8, mess.length() - 1);
		String clientIp = newMess.substring(0, newMess.indexOf(":"));
		String clientPort = newMess.substring(newMess.indexOf(":") + 1, newMess.length());
		Integer clientPortInt = Integer.parseInt(clientPort);
		// 拿到当前时间
		Date connectTime = new Date();
		Long workTime = System.currentTimeMillis();

		// 拿到接口放到set集合里
		Set<Object> interfaces = new HashSet<Object>();
		// 数据总量
		int count = 0;
		this.dpiMonitor(count, interfaces, clientIp, clientPortInt, connectTime);
		while (flag) {
			if (server.sdtpFlag) {
				logger.debug(channel.isOpen() + "服务器开启状态****************************************");
				logger.debug("1sdtp接口=开始==" + format.format(new Date()));
				SDTPMessage message = null;
				try {
					// 读取消息头以获取消息长度
					ByteBuffer temp = ByteBuffer.allocate(2);
					int length = channel.read(temp);
					if (length == -1) {
						throw new IOException("连接异常中断,未从输出流中读取到数据");
						// TODO数据异常
					}
					// 第一次读取数据如果没有把temp装满则继续读直到装满为止
					while (temp.position() < temp.capacity()) {// position位置 capacity容量
						logger.info("第一次接收长度小于2，继续等待接收length=" + length);
						length = channel.read(temp);
						if (length == -1) {
							logger.error("第一次只读取1个字节，循环后length == -1");
							throw new IndexOutOfBoundsException("接收数据包长度错误,第一次只读取1个字节，循环后length == -1");
						}
					}
					// 获取数据包总长度并构造缓冲区
					int totalLength = MessageUtil.toBigInteger(temp.array()).intValue();
//				logger.debug("2totalLength=="+totalLength);
					ByteBuffer byteArray = ByteBuffer.allocate(totalLength - 2);
//				logger.debug("3byteArray.position()=="+byteArray.position()+",byteArray.capacity()=="+byteArray.capacity());
					while (byteArray.position() < byteArray.capacity()) { // 只要未读满消息宣称的字节数就一直读取
//					logger.debug("4a---length:"+length + "----position-" + byteArray.position() + "===capacity=" + byteArray.capacity());
						length = channel.read(byteArray);
//					logger.debug("5b===length:"+length + "----position-" + byteArray.position() + "===capacity=" + byteArray.capacity());
						if (length == -1) {
							logger.error("length == -1");
							// 已到达输入流末尾
							if (byteArray.position() != byteArray.capacity()) {
								logger.error("接受数据包长度错误,预计长度: " + byteArray.capacity() + " ,实际长度: "
										+ byteArray.position() + ",字节内容:\n" + Arrays.toString(byteArray.array()));
								throw new IndexOutOfBoundsException("接收数据包长度错误");
							}
						}
					}
					logger.debug("6数据包长度:" + totalLength);
//				logger.debug("数据包长度:" + totalLength + ",字节内容:\n" + Arrays.toString(byteArray.array()));
					message = SDTPMessage.decode(totalLength, byteArray);

					// System.out.println("ip:"+channel.toString()+"接口:"+interfaceInt);
					logger.debug("7接收到请求包,数据类型:" + message.getMessageType().getDesc() + "\n" + message.toString());
					SDTPMessage respMsg = dealMessage(message);
//				if(respMsg != null)//华为测试，不返回信息
					channel.write(respMsg.getBuffer());
//				logger.debug("8准备发送响应包,数据类型:" + respMsg.getMessageType().getDesc() + ",数据包长度:" + respMsg.getTotalLength() + ",字节内容:\n" + Arrays.toString(respMsg.getBuffer().array()) + "\n" + respMsg.toString());

					// 每条数据解析完后拿到当前时间
					Long dataTime = System.currentTimeMillis();
					if (message.getMessageType().name().equals("notifyXDRData_Req")) {
						XDRDataNotifyReqMsg ss = (XDRDataNotifyReqMsg) message.getMsgBody();
						interfaces.addAll(ss.interfaces);
						count += totalLength;
					}
					// 数据解析完时间和业务时间作比较，如果时间差是1分钟，则计算速率
					if ((interfaces.size() > 0) && (dataTime - workTime) >= 60000) {
						this.dpiMonitor(count, interfaces, clientIp, clientPortInt, connectTime);
						workTime = System.currentTimeMillis();
						count = 0;
					}
				} catch (Exception e) {
					logger.error("链路异常，尝试发送链路响应!", e);
//						e.printStackTrace();
					try {
						// 按文档的意思好像不管出啥问题，只要链接没关都要尝试往客户端发个链接释放的响应来着？
						if (channel.isConnected()) {
							SDTPMessage respMsg = MessageUtil.dealLinkRel(message);
							channel.write(respMsg.getBuffer());
						}
					} catch (IOException e1) {
						logger.error("发送释放连接响应失败", e1);
						logger.debug(e1);
						// 业务监控 告警管理
						busWarnManage(interfaces, clientIp, clientPortInt);
					} finally {
						release();
					}
				}
			} else {
				release();
			}
		}

		logger.debug("==结束： ==" + format.format(new Date()));
	}

	/**
	 * @comment 处理消息
	 * 
	 * @param message
	 */
	private SDTPMessage dealMessage(SDTPMessage message) {
		SDTPMessage respMsg = null;
		// 过滤无效请求
		// checkAuth(message);
		logger.debug("message.getMessageType()===：" + message.getMessageType());
		switch (message.getMessageType()) {
		case verNego_Req:
			respMsg = MessageUtil.dealVerNego(message);
			authFlag = message.getMessageType().getValue();
			break;
		case linkAuth_Req:
			respMsg = MessageUtil.dealLinkAuth(message);
			authFlag = message.getMessageType().getValue();
			break;
		case linkCheck_Req:
			respMsg = MessageUtil.dealLinkCheck(message);
			break;
		case notifyXDRData_Req:
			recDataInfo++;
			respMsg = MessageUtil.dealXDRDataNoify(message);
//			respMsg = null;//华为测试，不返回信息
			break;
		case XDRRawDataSend_Req:
			recDataInfo++;
			respMsg = MessageUtil.dealXDRDataSend(message);
			break;
		case linkDataCheck_Req:
			respMsg = MessageUtil.dealLinkDataCheck(message, recDataInfo);
			recDataInfo = 0;// 链路数据发送校验数据清零
			break;
		case linkRel_Req:
			respMsg = MessageUtil.dealLinkRel(message);
			flag = false;
			break;
		default:
			logger.error("===错误的消息类型===" + message.getMessageType());
			throw new IllegalArgumentException("错误的消息类型");
		}
		return respMsg;
	}

	/**
	 * @comment 检查权限,版本,过滤无效请求
	 * @param message
	 */
	@SuppressWarnings("unused")
	private void checkAuth(SDTPMessage message) {
		int typeValue = message.getMessageType().getValue();
		if (authFlag < 2 && typeValue - authFlag > 1 && !message.getMessageType().equals(MessageType.linkRel_Req)) {
			throw new AccessControlException("消息包发送顺序不正确,版本验证或鉴权尚未完成");
		}
	}

	/**
	 * 关闭流
	 */
	public void release() {
		flag = false;
		try {
			if (channel != null) {
				channel.close();
				dpiMap.clear();
			}
		} catch (IOException e) {
			logger.error("关闭流失败：", e);
			e.printStackTrace();
		}
	}

	/**
	 * 打开或关闭sdtp开关
	 * 
	 * @return
	 * @throws IOException
	 */
	/*
	 * public int optNio(){ if(sdtpFlag==false){ sdtpFlag = true; return 1; }else {
	 * //release(); sdtpFlag = false; this.clearTable(); return 0; } }
	 */

	// 使用jdbc清空sys_monalarmlog表
	public void clearTable() {
		try {
			JdbcTemplate jdbcTemplate = new JdbcTemplate(ProtocolSpringContextUtils.getBean("dataSource"));
			// 数据清除
			String sql = "TRUNCATE TABLE sys_mondpilog";
			jdbcTemplate.execute(sql);
		} catch (Exception e) {
			logger.error("数据清除失败：", e);
			e.printStackTrace();
		}
	}

	/**
	 * 业务监控 告警管理
	 * 
	 * @param interfaces
	 * @param clientIp
	 * @param clientPortInt
	 */
	public void busWarnManage(Set<Object> interfaces, String clientIp, int clientPortInt) {
		// 1.连接断开,数据入库sys_monalarmlog
		// 处理set集合
		String setIntface = interfaces.toString();
		String property = setIntface.substring(1, setIntface.length() - 1);
		String propertyS = property.replace(",", "/");
		String description = clientIp + ":" + clientPortInt + "连接断开," + propertyS;
		// 拿到断开时间
		Date time = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String alarmTime = sdf.format(time);
		// 数据入库
		try {
			JdbcTemplate jdbcTemplate = new JdbcTemplate(ProtocolSpringContextUtils.getBean("dataSource"));
			String sql = "INSERT INTO sys_monalarmlog(DESCRIPTION,ALARM_TIME,STATUS) VALUES(?,?,?)";
			jdbcTemplate.update(sql, description, alarmTime, 0);
		} catch (Exception e2) {
			e2.printStackTrace();
		}
		// 2.删除sys_mondpilog对应的数据
		try {
			dpiMap.remove(clientIp + "#" + clientPortInt);
		} catch (Exception e3) {
			e3.printStackTrace();
		}
	}

	/**
	 * dpi 监控
	 * 
	 * @param dataTimeL
	 * @param workTimeL
	 * @param count
	 * @param interfaces
	 * @param clientIp
	 * @param clientPortInt
	 * @param connectTime
	 */
	public void dpiMonitor(int count, Set<Object> interfaces, String clientIp, int clientPortInt,
			Date connectTimeDate) {
		try {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			String connectTime = sdf.format(connectTimeDate);
			// 数据总量除以时间（秒），计算速率
			int rate = count / 60;
//				workTime = dataTime;
//				count = 0;
			// 处理set集合
			String setIntface = interfaces.toString();
			String property = setIntface.substring(1, setIntface.length() - 1);
			dpiMap.put(clientIp + "#" + clientPortInt, property + "#" + connectTime + "#" + rate);
			// 如果查询结果为空，则新增语句，不为空则做更新语句

		} catch (Exception e) {
			logger.info("sdtp dpi 监控数据入库报错！", e);
			e.printStackTrace();
		}

	}
}